﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Threading;

namespace PerformanceTest.Tools
{
    public class Tester : DependencyObject
    {
        public Tester()
        {
            this._steps = new List<Step>();
            this.ShowUiMessage = true;
            this.RecordTime = true;
        }

        private class Step
        {
            public Action<int> Action { get; set; }
            public int Repeats { get; set; }
        }

        private List<Step> _steps;
        private IEnumerator<Step> _enumerator;
        private int _currentRepeat;
        private DateTime _startTime;
        private long _startMemory;
        private Action<Action> _beginInvoke;

        public bool ShowUiMessage { get; set; }
        public bool RecordTime { get; set; }
        public bool RecordMemmory { get; set; }
        public int Delay { get; set; }

        public Action Completed { get; set; }
        public Tester AddAction(Action<int> action)
        {
            return this.AddAction(1, action);
        }
        public Tester AddAction(int repeats, Action<int> action)
        {
            this._steps.Add(new Step() {Action = action, Repeats = repeats});
            return this;
        }

        public void Start(bool asynStart = false)
        {
            if (this.Delay == 0)
            {
                this._beginInvoke = BeginInvokeNoDelay;
            }
            else
            {
                this._beginInvoke = GetBeginInvokeForDelay(this.Delay);
            }
            _enumerator = _steps.GetEnumerator();
            if (_enumerator.MoveNext())
            {
                _currentRepeat = 0;
                if (RecordMemmory)
                {
                    GC.Collect();
                    GC.WaitForPendingFinalizers();
                    GC.Collect();

                    _startMemory = GC.GetTotalMemory(true);
                }

                _startTime = DateTime.Now;
                if (asynStart)
                {
                    this._beginInvoke(new Action(Round));
                }
                else
                {
                    Round();
                }
            }
            else
            {
                throw new ArgumentException();
            }
        }
        private void Round()
        {
            this._enumerator.Current.Action(_currentRepeat);
            _currentRepeat++;
            if (_currentRepeat < _enumerator.Current.Repeats)
            {
                _beginInvoke(new Action(Round));
            }
            else
            {
                _currentRepeat = 0;
                if (this._enumerator.MoveNext())
                {
                    _beginInvoke(new Action(Round));
                }
                else
                {
                    // 最后一个动作不需要延迟
                    BeginInvokeNoDelay(OnCompleted);
                }
            }
        }
        private void OnCompleted()
        {
            var now = DateTime.Now;
            if (ShowUiMessage)
            {
                string message = "";

                if (RecordMemmory)
                {
                    GC.Collect();
                    GC.WaitForPendingFinalizers();
                    GC.Collect();

                    var current = GC.GetTotalMemory(true);
                    message = message + string.Format("Memory: {0:n};", (current - _startMemory)/1000.0);
                }
                if (RecordTime)
                {
                    if (RecordMemmory)
                    {
                        message = message + "\r\n";
                    }
                    message = message + string.Format("Time: {0:n} ms", (now - _startTime).TotalMilliseconds);
                }
                MessageBox.Show(message);
            }
            if (Completed != null)
            {
                Completed();
            }
        }

        private void BeginInvokeNoDelay(Action action)
        {
#if SILVERLIGHT
                this.Dispatcher.BeginInvoke(action);
#else
                this.Dispatcher.BeginInvoke(action, DispatcherPriority.Background);
#endif
        }
        private Action<Action> GetBeginInvokeForDelay(int delay)
        {
            return action =>
                       {
                           var timer = new DispatcherTimer() {Interval = TimeSpan.FromMilliseconds(delay)};
                           timer.Tick += (sender, e) =>
                                             {
                                                 action();
                                                 timer.Stop();
                                             };
                           timer.Start();
                       };
        }
    }
}